﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Painter3D.RenderSys;
using OpenTK;

namespace Painter3D
{
    public class Transform
    {
        public Vector3 position;
        public Quaternion quaternion;

        public Matrix4 Mat
        {
            get
            {
                return Matrix4.CreateFromQuaternion(quaternion) * Matrix4.CreateTranslation(position);
            }
        }
        /// <summary>
        /// 节点名称
        /// </summary>
        public string Name;
        /// <summary>
        /// 节点父级
        /// </summary>
        public Transform Parent;
        /// <summary>
        /// 节点子集
        /// </summary>
        public List<Transform> childs;
        /// <summary>
        /// 吸附的各种功能组件
        /// </summary>
        public List<Component> components
        {
            get; private set;
        }
        /// <summary>
        /// 节点是否启用
        /// </summary>
        public bool Enable = true;
        /// <summary>
        /// 节点是否向用户展示
        /// </summary>
        public bool UserVisable = false;

        /// <summary>
        /// 返回该节点的X轴在世界坐标系下的方向
        /// </summary>
        public Vector3 X
        {
            get
            {
                return new Vector3(GetGlobalMat() * Vector4.UnitX).Normalized();
            }
        }
        /// <summary>
        /// 返回该节点的Y轴在世界坐标系下的方向
        /// </summary>
        public Vector3 Y
        {
            get
            {
                return new Vector3(GetGlobalMat() * Vector4.UnitY).Normalized();
            }
        }
        /// <summary>
        /// 返回该节点的Z轴在世界坐标系下的方向
        /// </summary>
        public Vector3 Z
        {
            get
            {
                return new Vector3(GetGlobalMat() * Vector4.UnitZ).Normalized();
            }
        }


        public static Transform WorldTransform = new Transform("World");

        public Transform(string name, Transform parent = null)
        {
            position = Vector3.Zero;
            quaternion = Quaternion.Identity;
            Name = name ?? throw new ArgumentNullException(nameof(name));
            if (parent == null)
            {
                parent = WorldTransform;
            }
            Parent = parent;
            if (parent != null)
            {
                if (!parent.childs.Contains(this))
                {
                    parent.childs.Add(this);
                }
            }
            childs = new List<Transform>();
            components = new List<Component>();
        }

        public Transform(string name, Vector3 pos, Quaternion qua, Transform parent = null) : this(name, parent)
        {
            position = pos;
            quaternion = qua;
        }

        #region 组件查找
        /// <summary>
        /// 获取节点的组件
        /// </summary>
        /// <typeparam name="T">组件类型</typeparam>
        /// <returns>匹配的组件,null未查找到</returns>
        public T GetComponent<T>() where T : Component
        {
            foreach (var component in components)
            {
                if (component.GetType() == typeof(T))
                {
                    return component as T;
                }
            }
            return null;
        }
        /// <summary>
        /// 获取节点的组件
        /// </summary>
        /// <typeparam name="T">组件类型</typeparam>
        /// <returns>匹配的组件数组</returns>
        public T[] GetComponents<T>() where T : Component
        {
            List<T> results = new List<T>();
            foreach (var component in components)
            {
                if (component.GetType() == typeof(T))
                {
                    results.Add(component as T);
                }
            }
            return results.ToArray();
        }
        /// <summary>
        /// 该函数仅仅向components列表进行添加
        /// </summary>
        /// <param name="component">需要添加的组件列表</param>
        public void AddComponent(Component component)
        {
            if (component.GetType() == typeof(Render) && (components.Count == 0 || components[0].GetType() != typeof(Render)))
            {
                components.Insert(0, component);
            }
            else
            {
                components.Add(component);
            }
            if (component.Transform == null)
            {
                component.Transform = this;
            }
        }
        #endregion

        #region 节点遍历与查询
        /// <summary>
        /// 使用委托遍历自身和所有子节点,深度优先
        /// </summary>
        /// <param name="function">委托函数,返回true则停止遍历,示例：(transform,layer) => { } </param>
        /// <param name="layer">当前层级</param>
        /// <param name="enableNode">true时仅仅遍历启用的节点</param>
        /// <param name="userVisable">true时仅仅遍历用户可见的节点</param>
        public void LoopTreeWithF(Func<Transform, int, bool> function, int layer, bool enableNode = false, bool userVisable = false)
        {
            if (function(this, layer))
            {
                return;
            }
            for (int i = 0; i < childs.Count; i++)
            {
                if ((childs[i].Enable || !enableNode) && (childs[i].UserVisable || !userVisable))
                {
                    childs[i].LoopTreeWithF(function, layer + 1, enableNode, userVisable);
                }
            }
        }
        /// <summary>
        /// 使用委托遍历自身和所有子节点，深度优先
        /// </summary>
        /// <param name="function">委托函数,返回true则停止遍历,示例：(transform) => { } </param>
        /// <param name="enableNode">true时仅仅遍历启用的节点</param>
        /// <param name="userVisable">true时仅仅遍历用户可见的节点</param>
        public void LoopTreeWithF(Func<Transform, bool> function, bool enableNode = false, bool userVisable = false)
        {
            if (function(this))
            {
                return;
            }
            for (int i = 0; i < childs.Count; i++)
            {
                if ((childs[i].Enable || !enableNode) && (childs[i].UserVisable || !userVisable))
                {
                    childs[i].LoopTreeWithF(function, enableNode, userVisable);
                }
            }
        }
        /// <summary>
        /// 在子节点中查找
        /// </summary>
        /// <param name="name">节点名称</param>
        /// <returns>匹配的节点 null则未查找到</returns>
        public Transform FindInChild(string name)
        {
            foreach (var transform in childs)
            {
                if (transform.Name == name)
                {
                    return transform;
                }
            }
            return null;
        }
        /// <summary>
        /// 在子节点及以下层级中根据给定的路径查找节点
        /// </summary>
        /// <param name="path">节点路径'\'</param>
        /// <returns>匹配的节点 null则未查找到</returns>
        public Transform FindInChildByPath(string path)
        {
            MatchCollection matches = Regex.Matches(path, "([A-Za-z0-9])+");
            Transform transform = Transform.WorldTransform;
            for (int i = 0; i < matches.Count && transform != null; i++)
            {
                if (matches[i].Value != "World")
                {
                    transform = transform.FindInChild(matches[i].Value);
                }
            }
            if (transform != null)
            {
                return transform;
            }
            return null;
        }
        /// <summary>
        /// 根据给定的路径查找节点
        /// </summary>
        /// <param name="path">节点路径'\'</param>
        /// <returns>匹配的节点 null则未查找到</returns>
        public static Transform FindByPath(string path)
        {
            return WorldTransform.FindInChildByPath(path);
        }

        /// <summary>
        /// 判断该节点是否是给定节点的子节点
        /// </summary>
        /// <param name="transform">父节点</param>
        /// <returns>true 表明为子节点</returns>
        public bool IsTheChildOf(Transform transform)
        {
            Transform tempTrans = this;
            while (tempTrans.Parent != null)
            {
                if (tempTrans.Parent == transform)
                {
                    return true;
                }
                tempTrans = tempTrans.Parent;
            }
            return false;
        }

        /// <summary>
        /// 判断该节点是否是给定节点的子节点,重名会导致误判
        /// </summary>
        /// <param name="transform">父节点</param>
        /// <returns>true 表明为子节点</returns>
        public bool IsTheChildOf(string transform)
        {
            Transform tempTrans = this;
            while (tempTrans.Parent != null)
            {
                if (Parent.Name == transform)
                {
                    return true;
                }
                tempTrans = Parent;
            }
            return false;
        }

        #endregion

        #region 几何变换
        public enum ReferenceEnum
        {
            /// <summary>
            /// 当前自身参考系
            /// </summary>
            Self,
            /// <summary>
            /// 父级参考系
            /// </summary>
            Parent,
            /// <summary>
            /// 世界坐标系
            /// </summary>
            World,
        }
        /// <summary>
        /// 平移节点
        /// </summary>
        /// <param name="vector">移动向量</param>
        /// <param name="reference">参考系</param>
        public void Translate(Vector3 vector, ReferenceEnum reference = ReferenceEnum.Self)
        {
            switch (reference)
            {
                case ReferenceEnum.Self:
                    Vector4 increase = new Vector4(vector, 0.0f) * GetGlobalMat();
                    position += increase.Xyz;
                    break;
                case ReferenceEnum.Parent:
                    position += vector;
                    break;
                case ReferenceEnum.World:
                    throw new Exception("未定义");
                default:
                    break;
            }
        }
        /// <summary>
        /// 通过欧拉角度旋转节点
        /// </summary>
        /// <param name="eulerAngle">欧拉角度(deg)</param>
        /// <param name="reference">参考系</param>
        public void Rotate(Vector3 eulerAngle, ReferenceEnum reference = ReferenceEnum.Self)
        {
            switch (reference)
            {
                case ReferenceEnum.Self:
                    quaternion = quaternion * Quaternion.FromEulerAngles(new Vector3(MathHelper.DegreesToRadians(eulerAngle.X), MathHelper.DegreesToRadians(eulerAngle.Y), MathHelper.DegreesToRadians(eulerAngle.Z)));
                    break;
                case ReferenceEnum.Parent:
                    quaternion = Quaternion.FromEulerAngles(new Vector3(MathHelper.DegreesToRadians(eulerAngle.X), MathHelper.DegreesToRadians(eulerAngle.Y), MathHelper.DegreesToRadians(eulerAngle.Z))) * quaternion;
                    break;
                case ReferenceEnum.World:
                    throw new Exception("未定义");
                default:
                    break;
            }
        }
        /// <summary>
        /// 绕给定轴旋转节点
        /// </summary>
        /// <param name="axi">旋转轴</param>
        /// <param name="angle">旋转角度(deg)</param>
        /// <param name="reference">参考系</param>
        public void Rotate(Vector3 axi, float angle, ReferenceEnum reference = ReferenceEnum.Self)
        {
            switch (reference)
            {
                case ReferenceEnum.Self:
                    quaternion = quaternion * Quaternion.FromAxisAngle(axi, MathHelper.DegreesToRadians(angle));
                    break;
                case ReferenceEnum.Parent:
                    quaternion = Quaternion.FromAxisAngle(axi, MathHelper.DegreesToRadians(angle)) * quaternion;
                    break;
                case ReferenceEnum.World:
                    throw new Exception("未定义");
                default:
                    break;
            }
        }

        /// <summary>
        /// 设定节点的旋转
        /// </summary>
        /// <param name="eulerAngle">欧拉角XYZ</param>
        /// <param name="reference">参考系</param>
        public void Rotation(Vector3 eulerAngle, ReferenceEnum reference = ReferenceEnum.Self)
        {
            switch (reference)
            {
                case ReferenceEnum.Self:
                    throw new Exception("未定义");
                    break;
                case ReferenceEnum.Parent:
                    throw new Exception("未定义");
                    break;
                case ReferenceEnum.World:
                    throw new Exception("未定义");
                    break;
                default:
                    break;
            }
        }
        #endregion

        /// <summary>
        /// 获取全局矩阵，渲染循环里使用堆栈进行了优化
        /// </summary>
        /// <returns>全局矩阵</returns>
        public Matrix4 GetGlobalMat()
        {
            Matrix4 matTemp = Matrix4.Identity;
            Transform trans = this;
            while (trans != null)
            {
                matTemp = matTemp * trans.Mat;
                trans = trans.Parent;
            }
            return matTemp;
        }

        public Render AddRender(RenderSys.RenderObject.VertexData vertexData, RenderSys.Material material)
        {
            return new Render(vertexData, material, this);
        }
    }
}
